﻿using UnityEngine;
using System.Collections;
using System.Collections.Generic;

namespace MyAssetBundles {
    public class MyDependenciesManager : MonoBehaviour {
        protected Dictionary<string, MyDependenciesOperation> _dependenciesOperationDic;
        protected Dictionary<string, string> _dependenciesErrorDic;
        protected AssetBundleManifest _manifest = null;
        protected bool _isDirty = false;
        void Awake ()
        {
            _dependenciesErrorDic = new Dictionary<string, string>();
            _dependenciesOperationDic = new Dictionary<string, MyDependenciesOperation>();
        }

        public IEnumerator Reset ()
        {
            yield return InitManifest();
            _dependenciesErrorDic.Clear();
        }

        protected IEnumerator InitManifest ()
        {
            _manifest = null;
            AssetBundleCreateRequest acr = AssetBundle.LoadFromFileAsync(GetManifestPath());
            yield return acr;

            AssetBundle ass = acr.assetBundle;
            if (ass != null)
            {
                AssetBundleRequest abr = ass.LoadAssetAsync("AssetBundleManifest");
                yield return abr;
                _manifest = abr.asset as AssetBundleManifest;
                ass.Unload(false);
            }
            if (_manifest == null)
            {
                Debug.LogError("error: without asset bundle manifest file");
            }
        }
        
        public IEnumerator LoadDependencies (string assetBundleName)
        {
            if (_manifest == null) yield break;
            string[] dependencies = _manifest.GetAllDependencies(assetBundleName);
            if (dependencies.Length == 0) yield break;

            for (int i = 0; i < dependencies.Length; i++)
            {
                yield return LoadDependence(dependencies[i]);
            }
        }

        protected IEnumerator LoadDependence (string dependenceName)
        {
            MyDependenciesOperation operation = null;
            _dependenciesOperationDic.TryGetValue(dependenceName, out operation);
            if (operation != null)
            {
                operation.Retain();
                yield break;
            }
            string error = null;
            _dependenciesErrorDic.TryGetValue(dependenceName, out error);
            if (error != null) yield break;

            operation = new MyDependenciesOperation();
            operation.assetBundleName = dependenceName;
            operation.operation = AssetBundle.LoadFromFileAsync(GetAssetBundlePath(dependenceName));
            _dependenciesOperationDic.Add(dependenceName, operation);
            yield return operation.RaiseEvent();

            if (operation.error != null)
            {
                _dependenciesErrorDic.Add(dependenceName, operation.error);
                _dependenciesOperationDic.Remove(dependenceName);
            }
        }

        public IEnumerator ReloadDependencies (List<string> nameList)
        {
            yield return InitManifest();
            if (nameList.Count == 0) yield break;
            if (_dependenciesErrorDic.Count == 0) yield break;
            for (int i = 0; i < nameList.Count; i++)
            {
                if (MyAssetBundlesUtil.IsManifestFile(nameList[i])) continue;
                if (!_dependenciesErrorDic.ContainsKey(nameList[i])) continue;
                _dependenciesErrorDic.Remove(nameList[i]);
                yield return LoadDependence(nameList[i]);
                UnloadDependence(nameList[i]);
            }
            yield return new WaitForEndOfFrame();
            _isDirty = true;
        }

        public void UnloadDependencies (string assetBundleName)
        {
            if (_manifest == null) return;
            string[] dependencies = _manifest.GetAllDependencies(assetBundleName);
            for (int i = 0; i < dependencies.Length; i++)
            {
                UnloadDependence(dependencies[i]);
            }
            _isDirty = true;
        }

        protected void UnloadDependence (string dependenceName)
        {
            MyDependenciesOperation operation = null;
            _dependenciesOperationDic.TryGetValue(dependenceName, out operation);
            if (operation != null)
                operation.Release();
        }

        protected void CleanUnusedDependencies ()
        {
            _isDirty = false;
            List<string> keyList = new List<string>();
            foreach (var item in _dependenciesOperationDic)
            {
                if (item.Value.isUnused)
                    keyList.Add(item.Key);
            }

            foreach (var key in keyList)
            {
                MyDependenciesOperation operation = null;
                _dependenciesOperationDic.TryGetValue(key, out operation);
                if (operation == null || !operation.isUnused) continue;
                operation.Destroy();
                _dependenciesOperationDic.Remove(key);
            }
        }

        void Update ()
        {
            if (_isDirty) CleanUnusedDependencies();
            //Debug.Log(" ---  dependencies : " + _dependenciesOperationDic.Count);
        }

        protected string GetManifestPath ()
        {
            return GetAssetBundlePath(MyAssetBundlesConfig.PlatformName);
        }

        protected string GetAssetBundlePath (string abName)
        {
            return MyAssetBundlesManager.GetAssetBundlePath(abName);
        }
    }
}
